# LuckPay Web Manage System
#
# Copyright (c) 2016 Lucky Byte, Inc.
#
express = require 'express'
fs      = require 'fs'
path    = require 'path'
favicon = require 'serve-favicon'
rotator = require 'file-stream-rotator'
logger  = require 'morgan'
body_parser   = require 'body-parser'
cookie_parser = require 'cookie-parser'
pgsql   = require 'pg'
moment  = require 'moment'
session = require 'express-session'
pg_sess = require('connect-pg-simple')(session)
mkdirp  = require 'mkdirp'
multer  = require 'multer'
sysmenu = require './sysmenu'
constant = require './lib/constant'
merch_jobs = require './lib/merch_jobs'
terms_jobs = require './lib/terms_jobs'

webapp = express()
module.exports = webapp

# 加载配置文件
settings = require './etc/settings'
webapp.locals.web_logo = settings.web_logo
webapp.locals.web_title = settings.web_title
webapp.locals.web_title_color = settings.web_title_color
webapp.locals.web_subtitle = settings.web_subtitle
webapp.locals.web_subtitle_color = settings.web_subtitle_color

settings.start_time = moment()

# 运行环境
webapp.set 'env', settings.env

# 系统菜单
webapp.locals.sysmenu = sysmenu
webapp.locals.constant = constant

# 将设置保存全局变量以便访问
global.settings = settings
global.constant = constant

webapp.set 'trust proxy', settings.trust_proxy
webapp.set 'views', path.join(__dirname, 'views')
webapp.set 'view engine', 'jade'
webapp.disable 'x-powered-by'

# WEB 访问日志
if webapp.get('env') is 'development'
    webapp.use logger 'dev'
else
    access_log_stream = rotator.getStream
        date_format: 'YYYYMMDD'
        filename: settings.logs_dir + '/web_access-%DATE%.log'
        frequency: 'daily'
        verbose: true
    webapp.use logger('combined', stream: access_log_stream)

webapp.use favicon path.join(__dirname, 'public', settings.web_logo)
webapp.use body_parser.json()
webapp.use body_parser.urlencoded extended: true
webapp.use cookie_parser()
webapp.use express.static path.join(__dirname, 'public')

# SQL 类型转换, 1114 和 1184 为 timestamp 和 timestamptz
moment.fn.toJSON = -> return this.format('YYYY-MM-DD HH:mm:ss')
pgsql.types.setTypeParser 1114, (val) ->    # timestamp
    return if val then moment(val) else null
pgsql.types.setTypeParser 1184, (val) ->    # timestamptz
    return if val then moment(val) else null
pgsql.types.setTypeParser 1082, (val) ->    # date
    return if val then moment(val) else null
pgsql.types.setTypeParser 1083, (val) ->    # time
    return if val then moment(val, 'HH:mm:ss') else null

# 定时任务
if settings.master_server
    merch_jobs.add_all()
    terms_jobs.add_all()

# 会话配置
session_options =
    name: settings.session_name,
    secret: "Need Not Too Security",
    resave: false,
    saveUninitialized: false,
    rolling: true,
    cookie:
        secure: false,
        maxAge: 60 * 60 * 1000,
    store: new pg_sess
        pg: pgsql,
        conString: settings.pgsql_url,
        tableName: 'web_session',

# 会话设置
webapp.use session session_options

# 添加辅助函数
webapp.use (req, res, next) ->
    # 取当前日期/时间
    res.locals.today = ->
        moment().format('YYYY-MM-DD')
    res.locals.now = ->
        moment().format('YYYY-MM-DD HH:mm:ss')
    res.locals.yesterday = ->
        moment().subtract(1, 'days').format('YYYY-MM-DD')

    # 格式化金额，单位为元，输入参数可以为字符串或浮点数
    res.locals.fmtamt = (yuan) ->
        return '0.00' if not yuan
        num = parseFloat(yuan)
        num.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,')
    # 格式化金额，单位为分，输入参数可以为字符串或整数
    res.locals.fmtamt2 = (fen) ->
        return '0.00' if not fen
        num = parseInt(fen) / 100
        num.toFixed(2).replace(/(\d)(?=(\d{3})+\.)/g, '$1,')

    # 格式化显示POS报文中的10位交易时间(MMDDHHmmss)
    res.locals.fmttime10 = (timestr) ->
        return '' if not timestr
        return timestr.substring(0,2) + '-' + timestr.substring(2,4) + ' ' +
            timestr.substring(4,6) + ':' + timestr.substring(6,8) + ':' +
            timestr.substring(8,10)
    next()


# 登录页面不需要认证即可访问
webapp.use '/login', require './routes/login'

webapp.get '/logout', (req, res, next) ->
    req.session.destroy (err) ->
        return res.redirect '/login'

# 剩余的页面都需要认证后才能访问
webapp.use (req, res, next) ->
    userinfo = req.session.userinfo
    if not userinfo or not userinfo.uuid
        if req.xhr
            res.type('json')
            return res.json
                succ: false, errmsg: '会话超时，请重新登录', redirect: '/login'
        else
            req.session.last_access = req.url
            return res.redirect "/login"
    res.locals.userinfo = userinfo
    next()


# 删除上传数据中的空白
webapp.use (req, res, next) ->
    return next() if not req.body
    for k, v of req.body
        if typeof(v) is 'string' and req.body.hasOwnProperty(k)
            req.body[k] = v.trim()
    next()


# 系统统计信息
webapp.use (req, res, next) ->
    res.locals.uptime = moment().diff(settings.start_time, 'seconds')
    res.locals.memory_usage = process.memoryUsage()
    next()


# 全局处理
webapp.use (req, res, next) ->
    # 导航路径
    res.locals.breadcrumbs = []
    res.locals.breadcrumbs[0] = 'title': '主页', 'href': '/'

    # 主机列表
    if not webapp.locals.hosts
        await pgsql.connect settings.pgsql_url, defer err, client, done
        return done client and next err if err
        await client.query \
            "select * from pay_hosts order by serial", defer err, result
        return done client and next err if err
        done()
        webapp.locals.hosts = result.rows

    # 选择一个主机作为当前活动配置
    if not req.session.host
        for host in webapp.locals.hosts
            if host.master
                req.session.host = host
                break
    else
        for host in webapp.locals.hosts
            if host.uuid == req.session.host.uuid
                req.session.host = host
                break
    if not req.session.host
        req.session.host = webapp.locals.hosts[0]
    res.locals.host = req.session.host

    # 用户关联的快捷菜单
    res.locals.shortcuts = req.session.shortcuts
    if not res.locals.shortcuts
        await pgsql.connect settings.pgsql_url, defer err, client, done
        return done client and next err if err
        await client.query \
            "select shortcuts from web_users where uuid = $1", [
                req.session.userinfo.uuid
            ], defer err, result
        return done client and next err if err
        done()
        if result.rows.length > 0
            req.session.shortcuts = result.rows[0].shortcuts
        if not req.session.shortcuts
            req.session.shortcuts = []
        res.locals.shortcuts = req.session.shortcuts

    # 基于表格布局页面的公共变量
    res.locals.keyword   = req.body.keyword or req.query.keyword or ''
                                                           # 查询关键字
    res.locals.page_no   = parseInt(req.body.page_no) or 1 # 页面编号，从1开始
    res.locals.page_rows = req.body.page_rows or           # 每页行数
        parseInt(req.cookies['table.page_rows']) or 10
    res.locals.offset    =                                 # 起始记录偏移
        (res.locals.page_no - 1) * res.locals.page_rows
    res.locals.total     = 0                               # 总记录数，需补充
    res.locals.page_num  = 1                               # 总页数，需补充

    # 根据当前的路径设置一些公共的值
    found = false
    for menu in sysmenu
        break if found
        if menu.href
            if req.path == menu.href or req.path.startsWith menu.href + '/'
                res.locals.breadcrumbs.push
                    'title': menu.header, 'href': menu.href
        for item in menu.items
            if req.path.startsWith item.href
                res.locals.breadcrumbs.push
                    'title': item.title, 'href': item.href
                res.locals.search_form_action = req.path
                found = true
                break
    next()

# 查询站内通知信息
webapp.set 'notify_modified_time', moment().format('x')
webapp.use (req, res, next) ->
    # 如果通知修改过，则需要重新查询通知信息
    if req.session.notify_modified_time and req.session.notify
        sys_modified_time = webapp.get 'notify_modified_time'
        ses_modified_time = req.session.notify_modified_time
        if parseInt(sys_modified_time) < parseInt(ses_modified_time)
            res.locals.notify = req.session.notify
            req.session_modified = false
            return next()
    # 重置所有通知消息
    req.session.notify = {}
    req.session_modified = true

    # 更新用户最后查询时间
    req.session.notify_modified_time = moment().format('x')
    return next()

# 查询站内留言
webapp.use (req, res, next) ->
    return next() if req.session.notify.messages

    await pgsql.connect settings.pgsql_url, defer err, client, done
    if err then done(client); return next(err)

    await client.query \
        "select serial from web_messages where status = 1 and recver = $1", [
            req.session.userinfo.uuid
        ], defer err, result
    if err then done(client); return next(err)
    req.session.notify.messages = result.rows
    done()
    next()

# 已发送站内留言
webapp.use (req, res, next) ->
    return next() if req.session.notify.messages_send

    await pgsql.connect settings.pgsql_url, defer err, client, done
    if err then done(client); return next(err)

    await client.query \
        "select serial from web_messages where status = 1 and sender = $1", [
            req.session.userinfo.uuid
        ], defer err, result
    if err then done(client); return next(err)
    req.session.notify.messages_send = result.rows
    done()
    next()

# 系统通知，总是查询，因为有可能是其它系统发送了通知
webapp.use (req, res, next) ->
    await pgsql.connect settings.pgsql_url, defer err, client, done
    if err then done(client); return next(err)

    await client.query \
        "select serial from web_notify
            where status = 1 order by serial", defer err, result
    if err then done(client); return next(err)
    req.session.notify.notifications = result.rows

    await client.query \
        "select (
            select count(*)::int > 0 from web_notify
                where status = 1 and level > 1
        ) as warning, (
            select count(*)::int > 0 from web_notify
                where status = 1 and level > 2
        ) as error", defer err, result
    if err then done(client); return next(err)
    done()
    req.session.notify.warning = result.rows[0].warning
    req.session.notify.error = result.rows[0].error
    next()


# 如果当前用户有入户复核权限，查询待复核的入户信息
webapp.use (req, res, next) ->
    return next() if req.session.notify.wizard_wait_check

    # 检查用户是否具有入户复核权限
    wizcheck_check_perm = false
    if req.session.userinfo.role_root
        wizcheck_check_perm = true
    else
        for perm in req.session.userinfo.permission
            if perm.href is '/wizcheck'
                if perm.permission.access and perm.permission.check
                    wizcheck_check_perm = true
                break

    if not wizcheck_check_perm
        req.session.notify.wizard_wait_check = []
        return next()

    await pgsql.connect settings.pgsql_url, defer err, client, done
    if err then done(client); return next(err)

    await client.query \
        "select w.uuid from pay_wizard as w
            left join pay_merch_ex as m1 on m1.uuid = w.merid1
            left join pay_terms_ex as t1 on t1.uuid = w.termid1
            left join pay_merch_ex as m2 on m2.uuid = w.merid2
            left join pay_terms_ex as t2 on t2.uuid = w.termid2
          where w.inputer <> $1 and
            case when w.merch_type in (1, 2) then
                w.merid1  in (select uuid from pay_merch_ex where action = 1) and
                w.termid1 in (select uuid from pay_terms_ex where action = 1) and
                w.merid2  in (select uuid from pay_merch_ex where action = 1) and
                w.termid2 in (select uuid from pay_terms_ex where action = 1) and
                m1.rejected = false and t1.rejected = false and
                m2.rejected = false and t2.rejected = false
            end", [
            req.session.userinfo.uuid
        ], defer err, result
    if err then done(client); return next(err)
    req.session.notify.wizard_wait_check = result.rows
    done()
    next()


# 如果当前用户有商户复核权限，查询待复核的商户信息
webapp.use (req, res, next) ->
    return next() if req.session.notify.merchs_wait_check

    # 检查用户是否具有商户复核权限
    merch_check_perm = false
    if req.session.userinfo.role_root
        merch_check_perm = true
    else
        for perm in req.session.userinfo.permission
            if perm.href is '/merch'
                if perm.permission.access and perm.permission.check
                    merch_check_perm = true
                break

    if not merch_check_perm
        req.session.notify.merchs_wait_check = []
        return next()

    await pgsql.connect settings.pgsql_url, defer err, client, done
    if err then done(client); return next(err)

    await client.query \
        "select uuid from pay_merch_ex where
            checked = false and rejected = false and inputer <> $1", [
            req.session.userinfo.uuid
        ], defer err, result
    if err then done(client); return next(err)
    req.session.notify.merchs_wait_check = result.rows
    done()
    next()

# 查询用户被复核拒绝的商户信息
webapp.use (req, res, next) ->
    return next() if req.session.notify.merchs_rejected

    await pgsql.connect settings.pgsql_url, defer err, client, done
    if err then done(client); return next(err)

    await client.query \
        "select uuid from pay_merch_ex where rejected = true and inputer = $1", [
            req.session.userinfo.uuid
        ], defer err, result
    if err then done(client); return next(err)
    req.session.notify.merchs_rejected = result.rows
    done()
    next()

# 查询用户被复核通过的商户信息
webapp.use (req, res, next) ->
    return next() if req.session.notify.merchs_checked

    await pgsql.connect settings.pgsql_url, defer err, client, done
    if err then done(client); return next(err)

    await client.query \
        "select uuid from pay_merch_ex where checked = true and inputer = $1", [
            req.session.userinfo.uuid
        ], defer err, result
    if err then done(client); return next(err)
    req.session.notify.merchs_checked = result.rows
    done()
    next()

# 当前用户修改后待复核商户记录
webapp.use (req, res, next) ->
    return next() if req.session.notify.merchs_input

    await pgsql.connect settings.pgsql_url, defer err, client, done
    if err then done(client); return next(err)

    await client.query \
        "select uuid from pay_merch_ex where
            rejected = false and checked = false and inputer = $1", [
            req.session.userinfo.uuid
        ], defer err, result
    if err then done(client); return next(err)
    req.session.notify.merchs_input = result.rows
    done()
    next()

# 由用户复核待后待生效的商户记录
webapp.use (req, res, next) ->
    return next() if req.session.notify.merchs_wait_effect

    await pgsql.connect settings.pgsql_url, defer err, client, done
    if err then done(client); return next(err)

    await client.query \
        "select uuid from pay_merch_ex where checked = true and checker = $1", [
            req.session.userinfo.uuid
        ], defer err, result
    if err then done(client); return next(err)
    req.session.notify.merchs_wait_effect = result.rows
    done()
    next()

# 如果当前用户有终端复核权限，查询待复核的终端信息
webapp.use (req, res, next) ->
    return next() if req.session.notify.terms_wait_check

    # 检查用户是否具有终端的复核权限
    terms_check_perm = false
    if req.session.userinfo.role_root
        terms_check_perm = true
    else
        for perm in req.session.userinfo.permission
            if perm.href is '/terms'
                if perm.permission.access and perm.permission.check
                    terms_check_perm = true
                break

    if not terms_check_perm
        req.session.notify.terms_wait_check = []
        return next()

    await pgsql.connect settings.pgsql_url, defer err, client, done
    if err then done(client); return next(err)

    await client.query \
        "select uuid from pay_terms_ex where
            checked = false and rejected = false and inputer <> $1", [
            req.session.userinfo.uuid
        ], defer err, result
    if err then done(client); return next(err)
    req.session.notify.terms_wait_check = result.rows
    done()
    next()

# 查询用户被复核拒绝的终端信息
webapp.use (req, res, next) ->
    return next() if req.session.notify.terms_rejected

    await pgsql.connect settings.pgsql_url, defer err, client, done
    if err then done(client); return next(err)

    await client.query \
        "select uuid from pay_terms_ex where rejected = true and inputer = $1", [
            req.session.userinfo.uuid
        ], defer err, result
    if err then done(client); return next(err)
    req.session.notify.terms_rejected = result.rows
    done()
    next()

# 当前用户修改后待复核终端记录
webapp.use (req, res, next) ->
    return next() if req.session.notify.terms_input

    await pgsql.connect settings.pgsql_url, defer err, client, done
    if err then done(client); return next(err)

    await client.query \
        "select uuid from pay_terms_ex where
            rejected = false and checked = false and inputer = $1", [
            req.session.userinfo.uuid
        ], defer err, result
    if err then done(client); return next(err)
    req.session.notify.terms_input = result.rows
    done()
    next()

# 由用户复核待后待生效的终端记录
webapp.use (req, res, next) ->
    return next() if req.session.notify.terms_wait_effect

    await pgsql.connect settings.pgsql_url, defer err, client, done
    if err then done(client); return next(err)

    await client.query \
        "select uuid from pay_terms_ex where checked = true and checker = $1", [
            req.session.userinfo.uuid
        ], defer err, result
    if err then done(client); return next(err)
    req.session.notify.terms_wait_effect = result.rows
    done()
    next()


# 如果当前用户有消费代付复核权限，查询待复核的消费代付信息
webapp.use (req, res, next) ->
    return next() if req.session.notify.p1_wait_check

    # 检查用户是否具有消费代付的复核权限
    p1_check_perm = false
    if req.session.userinfo.role_root
        p1_check_perm = true
    else
        for perm in req.session.userinfo.permission
            if perm.href is '/p1'
                if perm.permission.access and perm.permission.check
                    p1_check_perm = true
                break

    if not p1_check_perm
        req.session.notify.p1_wait_check = []
        return next()

    await pgsql.connect settings.pgsql_url, defer err, client, done
    if err then done(client); return next(err)

    await client.query \
        "select uuid from pay_p1 where checked = false and inputer <> $1", [
            req.session.userinfo.uuid
        ], defer err, result
    if err then done(client); return next(err)
    req.session.notify.p1_wait_check = result.rows
    done()
    next()


# 当前用户修改后待复核消费代付记录
webapp.use (req, res, next) ->
    return next() if req.session.notify.p1_input

    await pgsql.connect settings.pgsql_url, defer err, client, done
    if err then done(client); return next(err)

    await client.query \
        "select uuid from pay_p1 where checked = false and inputer = $1", [
            req.session.userinfo.uuid
        ], defer err, result
    if err then done(client); return next(err)
    req.session.notify.p1_input = result.rows
    done()
    next()


# 如果当前用户有助农取款复核权限，查询待复核的记录
webapp.use (req, res, next) ->
    return next() if req.session.notify.p2_wait_check

    # 检查用户是否具有消费代付的复核权限
    p2_check_perm = false
    if req.session.userinfo.role_root
        p2_check_perm = true
    else
        for perm in req.session.userinfo.permission
            if perm.href is '/p2'
                if perm.permission.access and perm.permission.check
                    p2_check_perm = true
                break

    if not p2_check_perm
        req.session.notify.p2_wait_check = []
        return next()

    await pgsql.connect settings.pgsql_url, defer err, client, done
    if err then done(client); return next(err)

    await client.query \
        "select uuid from pay_p2 where checked = false and inputer <> $1", [
            req.session.userinfo.uuid
        ], defer err, result
    if err then done(client); return next(err)
    req.session.notify.p2_wait_check = result.rows
    done()
    next()


# 当前用户修改后待复核助农取款记录
webapp.use (req, res, next) ->
    return next() if req.session.notify.p2_input

    await pgsql.connect settings.pgsql_url, defer err, client, done
    if err then done(client); return next(err)

    await client.query \
        "select uuid from pay_p2 where checked = false and inputer = $1", [
            req.session.userinfo.uuid
        ], defer err, result
    if err then done(client); return next(err)
    req.session.notify.p2_input = result.rows
    done()
    next()


# 保存修改后的 session 数据
webapp.use (req, res, next) ->
    return next() if not req.session_modified
    req.session.save (err) ->
        res.locals.notify = req.session.notify
        next()


# 为所有菜单赋予当前用户的权限值，主要用于系统菜单状态
webapp.use (req, res, next) ->
    if req.session.userinfo.role_root
        return next()

    permission = req.session.userinfo.permission
    if not permission
        return req.session.destroy (err) ->
            return res.redirect "/login"
    for menu in sysmenu
        for item in menu.items
            item.permission = []
            for perm in permission
                if perm.href == item.href
                    item.permission = perm.permission
                    break
    next()


# 所有页面都是动态内容，关闭客户端缓存
# 'no-cache, no-store, must-revalidate'
webapp.use (req, res, next) ->
    res.append 'Cache-Control', 'must-revalidate'
    res.append 'max-age', '10'
    next()


# 这部分页面不需要验证用户权限
webapp.use '/',          require './routes/index'
webapp.use '/userinfo',  require './routes/userinfo'
webapp.use '/notify',    require './routes/notify'
webapp.use '/message',   require './routes/message'
webapp.use '/i',         require './routes/i'
webapp.use '/o',         require './routes/o'

# 这些关键字在 URL 中有特殊的含义
perm_path_prefixs = [
    'new', 'edit', 'delete', 'disable', 'check',
    'import', 'export', 'batch', 'recharge'
]

# 验证用户的访问权限
# 验证依赖 URL PATH 的命名规则约定
check_permission = (permission, subpath, href) ->
    if not permission or not permission.access
        return false
    if subpath.startsWith('/') then subpath = subpath.substring(1)

    for k in perm_path_prefixs
        if subpath == k or subpath.startsWith(k + '/')
            return permission[k]
    if subpath == 'input' or subpath.startsWith('input/')
        return permission.edit
    if subpath == 'enable' or subpath.startsWith('enable/')
        return permission.disable
    return true

# 权限验证
webapp.use (req, res, next) ->
    if req.session.userinfo.role_root
        res.locals.page_perms = root: true
        for k in perm_path_prefixs
            res.locals.page_perms[k] = true
        res.locals.page_perms_str = JSON.stringify res.locals.page_perms
        return next()
    permission = req.session.userinfo.permission
    if not permission
        return req.session.destroy (err) ->
            return res.redirect "/login"

    # 通过请求路径匹配系统配置的菜单项，如果能够匹配到，则需要验证权限
    for menu in sysmenu
        for item in menu.items
            if not req.path.startsWith(item.href)
                continue
            for perm in permission
                if perm.href != item.href
                    continue
                if not check_permission(perm.permission,
                    req.path.substring(item.href.length), item.href)
                    res.type('json') if req.xhr
                    return next(new Error('权限拒绝，请联系系统管理人员'))
                page_perms = perm.permission
                page_perms.root = req.session.userinfo.role_root
                res.locals.page_perms = page_perms
                res.locals.page_perms_str = JSON.stringify page_perms
                return next()
            # 没有匹配菜单项的记录，不能访问
            res.type('json') if req.xhr
            return next(new Error('权限拒绝，请联系系统管理人员'))

    # 如果不能匹配到系统菜单项，则不允许用户访问
    next(new Error('拒绝访问，您访问的地址不在系统服务范围内'))


# 路由配置
webapp.use '/i/091',     require './routes/i/091'
webapp.use '/i/096',     require './routes/i/096'
webapp.use '/i/351',     require './routes/i/351'

webapp.use '/o/091',     require './routes/o/091'
webapp.use '/o/096',     require './routes/o/096'
webapp.use '/o/351',     require './routes/o/351'
webapp.use '/o/071',     require './routes/o/071'

webapp.use '/wizard',    require './routes/wizard'
webapp.use '/wizcheck',  require './routes/wizcheck'
webapp.use '/merch',     require './routes/merch'
webapp.use '/terms',     require './routes/terms'

webapp.use '/p1',        require './routes/p1'
webapp.use '/p2',        require './routes/p2'
webapp.use '/p2/fund',   require './routes/p2/fund'
webapp.use '/p2/blacklist', require './routes/p2/blacklist'


webapp.use '/trlist',    require './routes/trlist'
webapp.use '/trstat/trcodes',      require './routes/trstat/trcodes'
webapp.use '/trstat/merch',        require './routes/trstat/merch'
webapp.use '/trstat/p1_order',     require './routes/trstat/p1_order'
webapp.use '/trstat/p1_order_sum', require './routes/trstat/p1_order_sum'
webapp.use '/trstat/p2_order',     require './routes/trstat/p2_order'
webapp.use '/trstat/p2_order_sum', require './routes/trstat/p2_order_sum'
webapp.use '/trstat/p2_fund_sum',  require './routes/trstat/p2_fund_sum'
webapp.use '/trstat',              require './routes/trstat'
webapp.use '/t40_recvr/done',      require './routes/t40_recvr/done'
webapp.use '/t40_recvr',           require './routes/t40_recvr'

webapp.use '/reports',             require './routes/reports'
webapp.use '/upay/acoma',          require './routes/upay/acoma'

webapp.use '/trctl_t',   require './routes/trctl_t'
webapp.use '/fee/set',   require './routes/fee/set'
webapp.use '/fee',       require './routes/fee'
webapp.use '/secure',    require './routes/secure'

webapp.use '/hosts',     require './routes/hosts'
webapp.use '/users',     require './routes/users'
webapp.use '/roles',     require './routes/roles'
webapp.use '/email',     require './routes/email'
webapp.use '/cardbin',   require './routes/cardbin'

webapp.use '/settings',  require './routes/settings'
webapp.use '/sched',     require './routes/sched'
webapp.use '/logs',      require './routes/logs'
webapp.use '/audit',     require './routes/audit'
webapp.use '/utils',     require './routes/utils'

webapp.use '/upgrade',   require './routes/upgrade'

webapp.use '/stats',     require './routes/stats'


# 路由不能匹配，返回 404
webapp.use (req, res, next) ->
    err = new Error '页面不存在'
    err.status = 404
    next err

# 开发模式错误处理
if webapp.get('env') is 'development'
    webapp.use (err, req, res, next) ->
        console.log err
        next err

# 生产模式错误处理
webapp.use (err, req, res, next) ->
    res.status err.status or 500
    if err.code == '23503'
        message = "操作违反外键约束，这通常是尝试去删除一条记录，" +
            "但这条记录尚被其它表关联引用，因此不能删除，" +
            "也有可能是其它原因导致，具体请查看错误日志"

    # JSON 错误返回
    if res.get('Content-Type') == 'application/json'
        # 响应错误消息也认为是成功的响应，否则客户端不能正确执行失败后续处理
        res.status 200
        return res.json succ: false, errmsg: message or err.message

    message = err.message if not message
    if message.length < 20
        err.message = ''
    else
        message = '系统运行时异常'

    res.render 'error', message: message, error: err
